﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.UI;
using System.Web.UI.WebControls;
using WikiPlex;
using WikiPlex.Compilation;
using WikiPlex.Compilation.Macros;
using WikiPlex.Formatting.Renderers;
using Altairis.Sewen.Configuration;
using System.Configuration;
using System.Web.Routing;

namespace Altairis.Sewen.WebControls {
    public class WikiMarkup : WebControl, ITextControl {

        public WikiMarkup() : base(HtmlTextWriterTag.Div) { }

        public string Text {
            get { return (string)this.ViewState["Text"]; }
            set { this.ViewState["Text"] = value; }
        }

        protected override void RenderContents(HtmlTextWriter writer) {
            var engine = new WikiPlex.WikiEngine();
            var html = engine.Render(this.Text, this.RegisterMacros(), this.RegisterRenderers());
            writer.Write(html);
        }

        protected virtual IEnumerable<IMacro> RegisterMacros() {
            return Macros.All.Union(new[] { new WikiLinkMacro() });
        }

        protected virtual IEnumerable<IRenderer> RegisterRenderers() {
            return Renderers.All.Union(new[] { new WikiLinkRenderer { ResolveWikiLink = this.ResolveLink } });
        }

        // Helper methods

        private string ResolveLink(string slug) {
            var config = ConfigurationManager.GetSection("altairis.sewen") as SewenConfigurationSection;

            if (config.WikiLink.ResolveMode == LinkResolveMode.Format) {
                // Format URL
                return this.ResolveUrl(string.Format(config.WikiLink.FormatString, slug));
            }
            else {
                // Use routing engine
                var values = new RouteValueDictionary();
                values.Add(config.WikiLink.SlugValueName, slug);
                return this.GetRouteUrl(config.WikiLink.RouteName, values);
            }
        }

        // Helper classes

        internal class WikiLinkMacro : IMacro {
            public const string SCOPE_NAME = "WikiLink";

            public string Id {
                get {
                    return "Altairis Sewen Rendering WikiLinkMacro";
                }
            }

            public virtual IList<MacroRule> Rules {
                get {
                    return new List<MacroRule> {         
                    new MacroRule(EscapeRegexPatterns.CurlyBraceEscape),         
                    new MacroRule(@"(?i)(\[)(?!\#|[a-z]+:)((?>[^\]]+))(\])",                 
                        new Dictionary<int, string> {                    
                            { 1, ScopeName.Remove },
                            { 2, SCOPE_NAME },
                            { 3, ScopeName.Remove }
                        })      
                    };
                }
            }

        }

        internal class WikiLinkRenderer : Renderer {
            private static string LINK_FORMAT_EXISTING = "<a href=\"{0}\" title=\"{2}\" class=\"internalLink\">{1}<span class=\"internalLinkIcon\"</span></a>";
            private static string LINK_FORMAT_NEW = "<a href=\"{0}\" class=\"internalNewLink\">{1}<span class=\"internalNewLinkIcon\"</span></a>";

            public Func<string, string> ResolveWikiLink { get; set; }

            protected override ICollection<string> ScopeNames {
                get {
                    return new[] { WikiLinkMacro.SCOPE_NAME };
                }
            }

            protected override string PerformExpand(string scopeName, string input, Func<string, string> htmlEncode, Func<string, string> attributeEncode) {
                var inputData = input.Split(new[] { '|' }, 2);
                string text, href, title = string.Empty;
                if (inputData.Length == 1) {
                    // No title specified
                    href = SewenPage.GetSlug(input);
                    text = input;
                }
                else {
                    // Title specified in form Title|link
                    href = SewenPage.GetSlug(inputData[1]);
                    text = inputData[0];
                }
                var page = PageStoreManager.LoadPage(href);
                if (page != null) title = page.Title;

                // Encode 
                text = htmlEncode(text);
                href = attributeEncode(this.ResolveWikiLink(href));
                title = attributeEncode(title);

                // Return formatted link
                if (page == null) {
                    return string.Format(LINK_FORMAT_NEW,
                        href,   // 0
                        text,   // 1
                        title); // 2
                }
                else {
                    return string.Format(LINK_FORMAT_EXISTING,
                        href,   // 0
                        text,   // 1
                        title); // 2
                }
            }

        }

    }
}
